/*
 * This file is part of the eccOS Library.
 * Copyright (C) 2007.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */
package org.openeccos.gui;

import java.util.Calendar;

import net.profidesk.app.PDSlider;
import nextapp.echo2.app.Alignment;
import nextapp.echo2.app.Border;
import nextapp.echo2.app.Button;
import nextapp.echo2.app.Color;
import nextapp.echo2.app.Column;
import nextapp.echo2.app.Component;
import nextapp.echo2.app.Extent;
import nextapp.echo2.app.FillImage;
import nextapp.echo2.app.Font;
import nextapp.echo2.app.Grid;
import nextapp.echo2.app.ImageReference;
import nextapp.echo2.app.Insets;
import nextapp.echo2.app.Label;
import nextapp.echo2.app.Row;
import nextapp.echo2.app.SplitPane;
import nextapp.echo2.app.event.ActionEvent;
import nextapp.echo2.app.event.ActionListener;
import nextapp.echo2.app.event.ChangeEvent;
import nextapp.echo2.app.event.ChangeListener;
import nextapp.echo2.app.event.TableModelEvent;
import nextapp.echo2.app.event.TableModelListener;
import nextapp.echo2.app.layout.SplitPaneLayoutData;
import nextapp.echo2.app.list.ListSelectionModel;

import org.openeccos.EccosAppInstance;
import org.openeccos.dao.EccosService;
import org.openeccos.dao.HeaderValue;
import org.openeccos.internal.EccleEditorPane;
import org.openeccos.internal.PDExpandableFilter;
import org.openeccos.model.IEccleEntry;
import org.openeccos.model.MBase;
import org.openeccos.properties.EccleProperties;
import org.openeccos.table.EccleTableModel;
import org.openeccos.util.EccosDefaultLookAndFeel;
import org.openeccos.util.ILookAndFeel;
import org.openeccos.util.IPageableFactory;
import org.openeccos.util.PDUtil;
import org.openeccos.widgets.PDTextField;

import com.sas.framework.expojo.ModelExposer;

import echopointng.ButtonEx;
import echopointng.ContainerEx;
import echopointng.LabelEx;
import echopointng.TableEx;
import echopointng.able.Positionable;
import echopointng.able.Scrollable;
import echopointng.layout.DisplayLayoutData;

/**
 * This class is a high-level component to display editable data in a convenient way. Features include:
 * - Built-in support to display a pageable list of expandable sections
 * - Very light-way loading of large data sets
 * - Built-in slidable filter
 * - Configurable toolbar
 *
 * @author cgspinner@web.de
 */
public abstract class EccLE extends PDWindowPane {

	public static enum DISPLAY_MODE {
		EXPANDABLE, SIDE_SCROLL, TOP_SCROLL;  //TOP_SCROLL is not implemented yet!
	}

	public static final String PROPERTY_DIALOG = "dialog";
	public static final String PROPERTY_MAXIMIZABLE = "maximizable";
	public static final String PROPERTY_MAXIMIZE_ICON = "maximizeIcon";
	public static final String PROPERTY_MAXIMIZE_ICON_INSETS = "maximizeIconInsets";
	public static final String PROPERTY_MAXIMIZED = "maximized";
	public static final String PROPERTY_MINIMIZABLE = "minimizable";
	public static final String PROPERTY_MINIMIZE_ICON = "minimizeIcon";
	public static final String PROPERTY_MINIMIZE_ICON_INSETS = "minimizeIconInsets";
	public static final String PROPERTY_MINIMIZED = "minimized";

	protected Column colHeaderAndTable;
	protected ILookAndFeel colorScheme = new EccosDefaultLookAndFeel();
	protected ContainerEx detailsContainer;  //this is used only by SIDE_SCROLL layout;
	protected DISPLAY_MODE displayMode = DISPLAY_MODE.EXPANDABLE;
	protected PDExpandableFilter expandableFilter;
	protected Column headerComponent;
	protected boolean ignoreSliderUpdate = false;
	protected String lastFilterText; // cache the last filter text; this avoids filtering for the same filter text twice
	protected LabelEx lblPosition;
	protected MBase model;
	protected EccleTableModel pageableTableModel;
	protected boolean showQuickFilter = false;
	protected PDSlider slider;
	protected SplitPane splitBodyAndFooter;
	protected SplitPane splitListAndDetails;
	protected PDTextField txtQuickSearch;
	protected TableEx table;
	protected ActionListener titleChangeListener;
	protected ActionListener dataChangeListener;
	protected IPageableFactory pageableFactory;

	public EccLE(String title, boolean showQuickFilter) {
		this(title, showQuickFilter, null);
	}

	public EccLE(String title, boolean showQuickFilter, MBase model) {
		this(title, showQuickFilter, model, DISPLAY_MODE.EXPANDABLE);
	}

	public EccLE(String title, boolean showQuickFilter, MBase model, DISPLAY_MODE displayMode) {
		this.displayMode = displayMode;
		init(title, showQuickFilter, model);
	}

	public void addFilter(String caption, Component filterComponent) {
		if (expandableFilter == null) {
			ActionListener okListener = new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					loadData();
				}
			};
			ActionListener resetListener = new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					resetFilter();
					loadData();
				}
			};
			expandableFilter = new PDExpandableFilter(okListener, resetListener);
			if (displayMode == DISPLAY_MODE.SIDE_SCROLL) {
				((SplitPaneLayoutData)colHeaderAndTable.getLayoutData()).setInsets(new Insets(22, 3, 0, 3));
			} else {
				colHeaderAndTable.setInsets(new Insets(22, 3, 0, 3));
			}
			add(expandableFilter);
		}
		expandableFilter.addFilter(caption, filterComponent);
	}

	protected void addHeaderComponent(Component child) {
		headerComponent.add(child);
	}

	public Button addToolButton(String text, String image) {
		return addToolButton(text, image, false);
	}

	protected void addToolLabel(String text) {
		LabelEx lbl = new LabelEx(text);
		lbl.setForeground(Color.WHITE);
		lbl.setHeight(new Extent(20));
		lbl.setOutsets(new Insets(3, 3, 0, 0));
		getToolbar().add(lbl);
	}

	protected void addNewItem(IEccleEntry item) {
		
		EccosService service = ((EccosService)ModelExposer.get().getService(EccosService.NAME));
		item.setNewEntryDate(Calendar.getInstance().getTime());
		service.persist(item);

		loadData();
		
		if (displayMode.equals(DISPLAY_MODE.EXPANDABLE)) {
			//pageableTableModel.updateExpandedState(item.getIdentifier());
		} else {			
			table.getSelectionModel().setSelectedIndex(0, true);
			showDetails(true);
		}
		updateRowDisplay();
	}
	
	protected void initTable() {
		dataChangeListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {				
				pageableTableModel.loadData();
				int rowCount = pageableTableModel.getRowCount();
				if (rowCount > 0 && table.getSelectionModel().getMinSelectedIndex() > (rowCount - 1)) {
					table.getSelectionModel().setSelectedIndex(rowCount - 1, true);
				}
				showDetails(false);
			}
		};

		pageableFactory = getFactory();
		pageableTableModel = new EccleTableModel(pageableFactory, displayMode, dataChangeListener);
		table = new TableEx(pageableTableModel);
		table.setHeaderVisible(false);
		table.setRolloverEnabled(false);
		table.setWidth(new Extent(100, Extent.PERCENT));
		table.setBorder(new Border(0, null, Border.STYLE_NONE));
		table.setInsets(new Insets(6, 2, 6, 2));
		table.setDefaultRenderer(Object.class, pageableTableModel);
		pageableTableModel.addTableModelListener(new TableModelListener() {
			public void tableChanged(TableModelEvent arg0) {
				updateNavigationBar();
			}
		});
		if (displayMode != DISPLAY_MODE.EXPANDABLE) {
			table.setSelectionBackground(new Color(225, 226, 250));
			//table.setSelectionBackgroundImage(new FillImage(PDUtil.getImage("img/semitrans4.png")));
			table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			table.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					showDetails(false);
				}
			});
		}
		table.setSelectionEnabled(displayMode != DISPLAY_MODE.EXPANDABLE);
	}

	protected void showDetails(boolean editMode) {
		if (displayMode != DISPLAY_MODE.SIDE_SCROLL) return;
		if (pageableTableModel.getRowCount() == 0) {
			return;
		}
		
		int row = table.getSelectionModel().getMinSelectedIndex();
		HeaderValue headerValue = (HeaderValue)pageableTableModel.getValueAt(0, row) ;
		EccleEditorPane exp = pageableFactory.getEditor(headerValue);

		//set the delete listener
		exp.setDataChangedListener(dataChangeListener);
		
		detailsContainer.removeAll();
		detailsContainer.add(exp);
		if (titleChangeListener == null) {
			titleChangeListener = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					HeaderValue headerValue = (HeaderValue)e.getSource();
					pageableTableModel.setValueAt(headerValue, 0, headerValue.row);
					pageableTableModel.fireTableCellUpdated(0, headerValue.row);
				}
			};
		}
		exp.setTitleChangeListener(titleChangeListener);
		
		exp.setExpanded(true);
		if (editMode) {
			exp.btnEditClicked();
		}
	}

	protected abstract IPageableFactory getFactory();

	public String getFilterText() {
		if (txtQuickSearch == null)
			return null;
		return txtQuickSearch.getText();
	}

	protected Column getHeaderComponent() {
		return headerComponent;
	}

	public ImageReference getMaximizeIcon() {
		return (ImageReference) getProperty(PROPERTY_MAXIMIZE_ICON);
	}

	public Insets getMaximizeIconInsets() {
		return (Insets) getProperty(PROPERTY_MAXIMIZE_ICON_INSETS);
	}

	public ImageReference getMinimizeIcon() {
		return (ImageReference) getProperty(PROPERTY_MINIMIZE_ICON);
	}

	public Insets getMinimizeIconInsets() {
		return (Insets) getProperty(PROPERTY_MINIMIZE_ICON_INSETS);
	}

	public PDExpandableFilter getSideFilter() {
		return expandableFilter;
	}

	protected void init(String title, boolean showQuickFilter, MBase model) {
		setTitle(title);
		this.showQuickFilter = showQuickFilter;
		this.model = model;
		initGUI();
		initGUI2();
		loadData();
		initTableNavigation();
	}

	protected final void initGUI() {

		splitBodyAndFooter = new SplitPane(SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP);
		splitBodyAndFooter.setSeparatorPosition(new Extent(23)); // hight of the footer
		add(splitBodyAndFooter);

		// left side is the search field, right side the navigation buttons
		Grid footerGrid = new Grid(2);
		footerGrid.setBackground(colorScheme.getBackgroundDark());
		footerGrid.setWidth(new Extent(100, Extent.PERCENT));
		footerGrid.setHeight(new Extent(23));
		footerGrid.setInsets(new Insets(6, 1, 6, 3));
		splitBodyAndFooter.add(footerGrid);

		// left side of the footer bar
		if (showQuickFilter) {
			Row filterBar = new Row();
			filterBar.setBackground(colorScheme.getBackgroundDark());
			filterBar.setInsets(new Insets(6, 0, 3, 1));
			filterBar.setCellSpacing(new Extent(6));
			footerGrid.add(filterBar);

			Label lblFilter = new Label(EccleProperties.quickSearch());
			lblFilter.setForeground(Color.WHITE);
			lblFilter.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, new Extent(11)));
			filterBar.add(lblFilter);

			txtQuickSearch = new PDTextField();
			txtQuickSearch.setBackgroundImage(new FillImage(PDUtil.getImage("img/textfield_bg.gif"), null, null, FillImage.NO_REPEAT));
			txtQuickSearch.setBackground(colorScheme.getBackgroundDark());
			txtQuickSearch.setWidth(new Extent(135));
			txtQuickSearch.setHeight(new Extent(18));
			txtQuickSearch.setInsets(new Insets(30, 0, 0, 0));
			txtQuickSearch.setBorder(new Border(0, null, Border.STYLE_NONE));
			txtQuickSearch.setActionCausedOnChange(true);
			txtQuickSearch.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent action) {
					if (getFilterText().equals(lastFilterText)) {
						return;
					}
					loadData();
					lastFilterText = getFilterText();
				}
			});
			filterBar.add(txtQuickSearch);
		} else {
			footerGrid.add(new Label(""));  //just fill the space
		}

		//right side of the footer bar
		lblPosition = new LabelEx("");
		lblPosition.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, new Extent(11)));
		lblPosition.setOutsets(new Insets(0, 0, 6, 0));
		lblPosition.setInsets(new Insets(0));
		lblPosition.setForeground(Color.WHITE);
		lblPosition.setBackground(colorScheme.getBackgroundDark());
		lblPosition.setTextAlignment(new Alignment(Alignment.RIGHT, Alignment.DEFAULT));
		footerGrid.add(lblPosition);

		ContainerEx splitItemsAndScrollbar = new ContainerEx();
		splitItemsAndScrollbar.setScrollBarPolicy(Scrollable.NEVER);
		if (EccosAppInstance.getInstance().isIE) {
			splitItemsAndScrollbar.setPosition(Positionable.FIXED);
		}

		initTable();
		DisplayLayoutData dld = new DisplayLayoutData();
		dld.setInsets(new Insets(0, 0, 19, 0));
		table.setLayoutData(dld);
		splitItemsAndScrollbar.add(table);

		slider = new PDSlider();
		slider.setOrientation(PDSlider.ORIENTATION_VERTICAL);
		slider.setImmediateNotification(true);
		slider.setWidth(new Extent(16));
		slider.setHeight(new Extent(100, Extent.PERCENT));
		slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent arg0) {
				if (!ignoreSliderUpdate) {
					pageableTableModel.setCurrentTopRow(slider.getValue() - 1);
					showDetails(false);
				}
			}
		});
		dld = new DisplayLayoutData();
		dld.setPosition(Positionable.ABSOLUTE);
		dld.setRight(new Extent(0));
		dld.setBottom(new Extent(0));
		dld.setHeight(new Extent(100, Extent.PERCENT));
		slider.setLayoutData(dld);
		slider.setHeight(new Extent(100, Extent.PERCENT));
		splitItemsAndScrollbar.add(slider);

		colHeaderAndTable = new Column();
		headerComponent = new Column();
		colHeaderAndTable.add(headerComponent);
		colHeaderAndTable.add(splitItemsAndScrollbar);

		if (displayMode == DISPLAY_MODE.SIDE_SCROLL) {
			splitListAndDetails = new SplitPane();
			splitListAndDetails.setSeparatorPosition(new Extent(240));
			splitListAndDetails.setSeparatorColor(Color.LIGHTGRAY);
			splitListAndDetails.setSeparatorWidth(new Extent(2));
			splitBodyAndFooter.add(splitListAndDetails);

			SplitPaneLayoutData spld = new SplitPaneLayoutData();
			spld.setOverflow(SplitPaneLayoutData.OVERFLOW_HIDDEN);
			colHeaderAndTable.setLayoutData(spld);
			splitListAndDetails.add(colHeaderAndTable);

			detailsContainer = new ContainerEx();
			detailsContainer.setInsets(new Insets(6));
			detailsContainer.setScrollBarPolicy(Scrollable.NEVER);
			splitListAndDetails.add(detailsContainer);
		} else {
			splitBodyAndFooter.add(colHeaderAndTable);
		}
	}

	protected void initGUI2() {
	}

	public void initTableNavigation() {
		slider.setValue(1);
		updateNavigationBar();
	}

	public boolean isDialog() {
		Boolean value = (Boolean) getProperty(PROPERTY_DIALOG);
		return value == null ? false : value.booleanValue();
	}

	public boolean isMaximizable() {
		Boolean value = (Boolean) getProperty(PROPERTY_MAXIMIZABLE);
		return value == null ? true : value.booleanValue();
	}

	public boolean isMaximized() {
		Boolean value = (Boolean) getProperty(PROPERTY_MAXIMIZED);
		return value == null ? true : value.booleanValue();
	}

	public boolean isMinimizable() {
		Boolean value = (Boolean) getProperty(PROPERTY_MINIMIZABLE);
		return value == null ? true : value.booleanValue();
	}

	public boolean isMinimized() {
		Boolean value = (Boolean) getProperty(PROPERTY_MINIMIZED);
		return value == null ? true : value.booleanValue();
	}

	/*
	 * Override this method if you do not use byCriteria
	 */
	protected void loadData() {
		pageableTableModel.setCurrentTopRow(0);
		table.getSelectionModel().setSelectedIndex(0, true);
		showDetails(false);
	}

	/**
	 * @see nextapp.echo2.app.Component#processInput(java.lang.String,
	 *      java.lang.Object)
	 */
	@Override
	public void processInput(String inputName, Object inputValue) {
		if (INPUT_CLOSE.equals(inputName)) {
			userClose();
		} else if (PROPERTY_POSITION_X.equals(inputName)) {
			setPositionX((Extent) inputValue);
		} else if (PROPERTY_POSITION_Y.equals(inputName)) {
			setPositionY((Extent) inputValue);
		} else if (PROPERTY_WIDTH.equals(inputName)) {
			setWidth((Extent) inputValue);
		} else if (PROPERTY_HEIGHT.equals(inputName)) {
			setHeight((Extent) inputValue);
		} else if (Z_INDEX_CHANGED_PROPERTY.equals(inputName)) {
			setZIndex(((Integer) inputValue).intValue());
		}
	}

	protected void resetFilter() {
		// override if applicable
	}

	public void setDialog(boolean newValue) {
		setProperty(PROPERTY_DIALOG, new Boolean(newValue));
	}

	public void setMaximizable(boolean newValue) {
		setProperty(PROPERTY_MAXIMIZABLE, new Boolean(newValue));
	}

	public void setMaximized(boolean newValue) {
		setProperty(PROPERTY_MAXIMIZED, new Boolean(newValue));
	}

	public void setMinimizable(boolean newValue) {
		setProperty(PROPERTY_MINIMIZABLE, new Boolean(newValue));
	}

	public void setMinimized(boolean newValue) {
		setProperty(PROPERTY_MINIMIZED, new Boolean(newValue));
	}


	protected void updateNavigationBar() {
		// show or hide the scrolling bar
		if (pageableTableModel.getAllRowsCount() <= pageableTableModel.getRowCount()) {
			slider.setVisible(false);
		} else {
			slider.setVisible(true);
			ignoreSliderUpdate = true;
			slider.setMinimum(1);
			slider.setMaximum(pageableTableModel.getAllRowsCount() + 1);
			slider.setValue(pageableTableModel.getCurrentTopRow() + 1);
			slider.setExtent(pageableTableModel.getRowCount());
			slider.setIncrement(pageableTableModel.getRowCount());
			ignoreSliderUpdate = false;
		}
		updateRowDisplay();
	}

	protected void updateRowDisplay() {
		int pos = pageableTableModel.getCurrentTopRow();
		String s = "";
		if (pageableTableModel.getAllRowsCount() == 0) {
			s = "(No data)";
		} else if (pos == 0 && pageableTableModel.getRowCount() == pageableTableModel.getAllRowsCount()) {
			s = " (Total: " + pageableTableModel.getAllRowsCount() + ")";
		} else {
			s = EccleProperties.display() + " " + (pos + 1) + " - ";
			s += pageableTableModel.getRowCount() + pos;
			s += " (Total: " + pageableTableModel.getAllRowsCount() + ")";
		}
		lblPosition.setText(s);
	}

	public class NavigationButton extends ButtonEx {
		NavigationButton(String icon) {
			super(PDUtil.getImage(icon));
			setBackground(Color.WHITE);
			setInsets(new Insets(0, 0, 0, 0));
			setOutsets(new Insets(0, 0, 0, 0));
			setBorder(new Border(new Extent(0), new Color(0, 0, 0), Border.STYLE_NONE));
			setRolloverEnabled(false);
		}
	}
}
